package cn.edu.zut.sunday.news.service.impl;

import cn.edu.zut.sunday.constant.NewsConstant;
import cn.edu.zut.sunday.news.dao.NewsCategoryRepository;
import cn.edu.zut.sunday.news.domain.NewsCategory;
import cn.edu.zut.sunday.news.service.ImportNewsService;
import cn.edu.zut.sunday.news.vo.NewsVo;
import cn.edu.zut.sunday.utils.ReloadFileUtils;
import com.google.common.collect.Sets;
import com.google.gson.Gson;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class ImportNewsServiceImpl implements ImportNewsService {


    private NewsCategoryRepository newsCategoryRepository;

    private static final Gson GSON = new Gson();

    @Autowired
    public void setNewsCategoryRepository(NewsCategoryRepository newsCategoryRepository) {
        this.newsCategoryRepository = newsCategoryRepository;
    }

    @Override
    public boolean initDatabase() {
        try {
            List<File> files = ReloadFileUtils.getFiles(NewsConstant.DATASET_FILE_PATH);
            // 没有文件则直接返回，不执行加载数据操作
            if (files.size() == 0) {
                return true;
            }
            // 清空数据库，这里删除newsCategory则会级联删除相关news
            newsCategoryRepository.deleteAll();

            // 处理文件信息
            files.stream()
              // 过滤相关后缀名文件
              .filter(file -> file.getName().endsWith(NewsConstant.DATASET_FILE_SUFFIX))
              //读取每一个文件并保存到数据库中
              .forEach(file -> {
                  try {
                      final List<String> lines = ReloadFileUtils.readFileAllLines(file);
                      // 把这个文件的数据写入到数据库中
                      dealLinesInfo2DB(lines);
                  } catch (IOException e) {
                      log.error("read dataset failed. path is: [ {} ], error: {}",
                        file.getName(), e.getMessage());
                  }
              });
            return true;
        } catch (Exception e) {
            log.error("read dataset failed. path is: [ {} ], error: {}",
              NewsConstant.DATASET_FILE_PATH, e.getMessage());
        }
        return false;
    }

    private void dealLinesInfo2DB(List<String> lines) {
        //每一行都是一个对象，反序列化成对象
        // 这里需要多做一个步骤：就是创建News对象的时候也需要把类别抽离出来
        final Map<String, List<NewsVo>> category2NewsVoMap = lines.stream()
          .map(line -> GSON.fromJson(line, NewsVo.class))
          .collect(Collectors.groupingBy(NewsVo::getCategory));
        // 构建NewsCategory实体信息
        category2NewsVoMap.keySet()
          .forEach(category -> {
              final NewsCategory newsCategory = new NewsCategory();
              // 设置类别
              newsCategory.setCategory(category);
              // 获取所属类别的所有News
              final List<NewsVo> newsVoList = category2NewsVoMap.get(category);
              // 设置count
              newsCategory.setCount(newsVoList.size());
              // 给每一个News对象设置外键
              newsVoList.forEach(newsVo -> {
                  newsVo.setNewsCategory(newsCategory);
                  // 这是是防止数据太长，做一个截断操作
                  newsVo.setContent(newsVo.getContent()
                    .substring(0, Math.min(1024, newsVo.getContent().length())));
              });

              // 这里是设置所有属于这个类别的实体，在插入category时，也会把这些实体插入到数据库中
              // 这个地方使用了一个技巧，由于NewVo和News是父子关系，
              // 所以直接NewVo强转News，直接把字符类型的category擦出了
              newsCategory.setNewsSet(Sets.newHashSet(newsVoList));
              // 把这个Category实体存入数据库中
              newsCategoryRepository.save(newsCategory);
          });
    }


    @Override
    public String datasetName() {
        return NewsConstant.DATASET_NAME;
    }
}
